#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>

typedef struct edge_node
{
    int incr_vert, vertex; // 生长点和非生长点
    int cost;              // 边的花费
} edge;                    // 边节点的类型结构

void Prim(int M, int n)
{
    int p, q, w;
    int L[25][25] = {0}; // 初始化邻接矩阵
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            if (i != j)
                L[i][j] = 1000; // 设置非自环边的初始花费为1000
        }
    }
    for (int i = 0; i < M; i++) // 读取边的花费并更新邻接矩阵
    {
        scanf("%d%d%d", &p, &q, &w);
        L[p - 1][q - 1] = w;
        L[q - 1][p - 1] = w;
    }

    int v, i, j, k;
    edge t, wait[100];          // 待选边表
    for (v = 0; v < n - 1; v++) // 初始化待选边表
    {
        wait[v].incr_vert = 0;
        wait[v].vertex = v + 1;
        wait[v].cost = L[0][v + 1];
    }
    for (i = 0; i < n - 2; i++)
    {
        k = i; // 找到最短的待选边
        for (j = i + 1; j < n - 1; j++)
        {
            if (wait[j].cost < wait[k].cost)
                k = j;
        }
        t = wait[k];
        wait[k] = wait[i];
        wait[i] = t;                    // 交换待选边
        v = wait[i].vertex;             // 新的生长点
        for (j = i + 1; j < n - 1; j++) // 更新待选边表
            if (wait[j].cost > L[v][wait[j].vertex])
            {
                wait[j].cost = L[v][wait[j].vertex];
                wait[j].incr_vert = v;
            }
    }
    for (i = 0; i < n - 1; i++)
    {
        if (i > 0)
            printf("\n");
        printf("%d,%d,%d",
               (wait[i].incr_vert + 1) < (wait[i].vertex + 1) ? (wait[i].incr_vert + 1) : (wait[i].vertex + 1),
               (wait[i].incr_vert + 1) > (wait[i].vertex + 1) ? (wait[i].incr_vert + 1) : (wait[i].vertex + 1),
               wait[i].cost);
    }
}

int main()
{
    int N, M;
    scanf("%d%d", &N, &M); // 读取村庄数和道路数

    Prim(M, N);

    return 0;
}